package com.situ.company.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.situ.company.util.StringUtil;


import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Enumeration;

/**
 * 封装此父类　实现对HttpServlet类的基本继承，并重写doGet与doPost方法
 * <p>
 * 当一个子类继承此父类时，可实现根据请求参数cmd的值　执行子类中对应名称的方法的功能
 * <p>
 * 需要注意：子类中不能出现同名方法（方法不要重载）
 */
public class BaseController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        //只要是请求/store　就进到doGet
        String cmd = req.getParameter("cmd");
        try {
            //获取当前类中所有的方法：然后遍历，找方法名与cmd值一样的那个方法
            Method[] ms = this.getClass().getDeclaredMethods();
            Method m = null;
            for (Method temp : ms) {
                if (temp.getName().equals(cmd)) {
                    m = temp;
                    break;
                }
            }
            if (m == null) {
                System.out.println("未找到对应cmd的方法");
            } else {
                m.setAccessible(true);
                //通过反射执行Controller中的方法，优化参数问题
                invokeMethod(m, req, resp);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //通过反射执行Controller的方法
    private void invokeMethod(Method m, HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException, IOException {
        // 获取方法中参数列表
        Parameter[] params = m.getParameters();
        // 获取方法中的参数的类型
        Class<?>[] types = m.getParameterTypes();
        // 创建一个新数组，装实参
        Object[] param = new Object[params.length];
        for (int i = 0; i < params.length; i++) {

            // 处理int 和double的基本类型
            if (types[i].equals(int.class)) {
                param[i] = 0;
            } else if (types[i].equals(double.class)) {
                param[i] = 0.0;
            }

            // 当前参数的参数名，要配合eclipse中 设置－java-complier中的设置使用
            String name = params[i].getName();
            if (types[i].equals(String.class)) {
                param[i] = req.getParameter(name);
            } else if (types[i].equals(Integer.class) || types[i].equals(int.class)) {
                String value = req.getParameter(name);
                if (StringUtil.isNotBlank(value)) {
                    param[i] = Integer.parseInt(req.getParameter(name));
                }
            } else if (types[i].equals(HttpServletRequest.class)) {
                param[i] = req;
            } else if (types[i].equals(HttpServletResponse.class)) {
                param[i] = resp;
            } else if (types[i].equals(Double.class) || types[i].equals(double.class)) {
                String value = req.getParameter(name);
                if (StringUtil.isNotBlank(value)) {
                    param[i] = Double.parseDouble(value);
                }
            } else if (types[i].equals(HttpSession.class)) {
                param[i] = req.getSession();
            } else {// 对象类型，先new 对象
                try {
                    Object o = types[i].newInstance();
                    // 获取请求中所有的参数的名字
                    Enumeration<String> names = req.getParameterNames();
                    while (names.hasMoreElements()) {
                        // pName:请求参数名
                        String pName = names.nextElement();
                        try {
                            // 找一下这个类中有没有这个请求参数的名对应的成员变量
                            Field field = types[i].getDeclaredField(pName);
                            field.setAccessible(true);
                            String value = req.getParameter(pName);
                            // 把值包进对象中，只支持如下：Integer,Double,String,Date
                            if (field.getType().equals(Integer.class) && StringUtil.isNotBlank(value)) {
                                field.set(o, Integer.parseInt(value));
                            } else if (field.getType().equals(Double.class) && StringUtil.isNotBlank(value)) {
                                field.set(o, Double.parseDouble(value));
                            } else if (field.getType().equals(Date.class) && StringUtil.isNotBlank(value)) {
                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                                field.set(o, sdf.parse(value));
                            } else if (field.getType().equals(LocalDateTime.class) && StringUtil.isNotBlank(value)) {
                                field.set(o,
                                        LocalDateTime.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                            } else {
                                field.set(o, value);
                            }
                        } catch (Exception e) {

                        }
                    }
                    param[i] = o;
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("封装BasicController中反射创建方法中的对象异常");
                }
            }
        }
        try {
            Object result = m.invoke(this, param);
            // 如果执行方法后有返回值
            if (result != null) {
                resp.setContentType("application/json;charset=utf8");
                resp.getWriter().write(new ObjectMapper().writeValueAsString(result));
            }
        } catch (Exception e) {
            System.out.println("封装BasicController中反射执行方法报错");
            e.printStackTrace();
        }
    }


    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}